package com.gitee.mp.core.parser;

import cn.hutool.core.annotation.AnnotationUtil;
import com.gitee.mp.annotation.Collection;
import com.gitee.mp.annotation.Field;
import com.gitee.mp.annotation.Index;
import com.gitee.mp.domain.definition.CollectionDefinition;
import com.gitee.mp.domain.definition.FieldDefinition;
import com.gitee.mp.domain.definition.IndexDefinition;
import com.gitee.mp.extend.ExtendParamsGenerator;
import com.gitee.mp.extend.DefinitionParser;
import com.gitee.mp.infra.constants.MilvusPlusConstants;
import com.gitee.mp.infra.utils.ApplicationContextUtils;
import com.gitee.mp.infra.utils.MilvusCommonUtils;
import io.milvus.exception.UnExpectedException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static cn.hutool.core.text.CharSequenceUtil.emptyToDefault;

/**
 * @author hongda.li
 */
@Slf4j
@Getter
public class DefaultDefinitionParser implements DefinitionParser {

    private volatile boolean parsed = false;

    private final CollectionDefinition collectionDefinition;

    private final List<FieldDefinition> fieldDefinitionList;

    private final List<IndexDefinition> indexDefinitionList;

    public DefaultDefinitionParser() {
        this.collectionDefinition = new CollectionDefinition();
        this.fieldDefinitionList = new ArrayList<>();
        this.indexDefinitionList = new ArrayList<>();
    }

    @Override
    public CollectionDefinition parse(final Class<?> clazz) {
        if (this.parsed) {
            return collectionDefinition;
        }

        // 解析集合
        this.parseCollection(clazz);

        // 解析字段
        this.parseFieldList(clazz);
        this.collectionDefinition.setFieldDefinitionList(this.fieldDefinitionList);

        // 解析索引
        this.parseIndexList(clazz);
        this.collectionDefinition.setIndexDefinitionList(this.indexDefinitionList);

        // 解析完成
        this.parsed = true;

        return collectionDefinition;
    }

    protected void parseCollection(Class<?> clazz) {
        Collection collection = AnnotationUtil.getAnnotation(clazz, Collection.class);
        Objects.requireNonNull(collection);

        String collectionName = emptyToDefault(collection.name(), clazz.getSimpleName());
        this.collectionDefinition.setCollectionName(collectionName);
        this.collectionDefinition.setConsistencyLevel(collection.level());
        this.collectionDefinition.setCollectionDescription(collection.description());
        this.collectionDefinition.setCreateIfNotExists(collection.createIfNotExists());
        this.collectionDefinition.setReloadIfNotAccess(collection.reloadIfNotAccess());
    }

    protected void parseFieldList(Class<?> clazz) {
        MilvusCommonUtils.processField(clazz, Field.class, field -> {
            Field annotation = AnnotationUtil.getAnnotation(field, Field.class);

            String fieldName = emptyToDefault(annotation.name(), field.getName());

            // 重名字段校验
            if (this.fieldDefinitionList.stream().anyMatch(item -> Objects.equals(item.getFieldName(), fieldName))) {
                log.error("The field with the same name exists - [{} - {}]", clazz.getName(), fieldName);
                throw new UnExpectedException(fieldName);
            }

            FieldDefinition definition = new FieldDefinition();
            definition.setFrom(field.getName());
            definition.setFieldName(annotation.name());
            definition.setType(annotation.type());
            definition.setPrimary(annotation.isPrimary());
            definition.setDynamic(annotation.isDynamic());
            definition.setMaxLength(annotation.maxLength());
            definition.setDimension(annotation.dimension());
            definition.setDescription(annotation.description());
            definition.setAutoIncrement(annotation.autoIncrement());
            definition.setPartitionKey(annotation.isPartitionKey());

            // 扩展配置
            if (!ExtendParamsGenerator.class.equals(annotation.generator())) {
                ExtendParamsGenerator generator = ApplicationContextUtils.findBeanFromCache(annotation.generator());
                definition.setExtendParams(generator.generate());
            }

            this.fieldDefinitionList.add(definition);
        });
    }

    protected void parseIndexList(Class<?> clazz) {
        MilvusCommonUtils.processField(clazz, Index.class, field -> {
            Index annotation = AnnotationUtil.getAnnotation(field, Index.class);

            String indexName = emptyToDefault(annotation.name(), field.getName() + MilvusPlusConstants.INDEX_SUFFIX);

            // 重名索引校验
            if (this.indexDefinitionList.stream().anyMatch(item -> Objects.equals(item.getIndexName(), indexName))) {
                log.error("The index with the same name exists - [{} - {}]", clazz.getName(), indexName);
                throw new UnExpectedException(indexName);
            }

            IndexDefinition definition = new IndexDefinition();
            definition.setIndexName(indexName);
            definition.setType(annotation.type());
            definition.setTarget(field.getName());

            // 扩展配置
            if (!ExtendParamsGenerator.class.equals(annotation.generator())) {
                ExtendParamsGenerator generator = ApplicationContextUtils.findBeanFromCache(annotation.generator());
                definition.setExtendParams(generator.generate());
            }
        });
    }
}
